from machine import I2C, Pin
from ssd1306 import SSD1306_I2C
import utime
import math
import random
import gc
import framebuf


oled = None

def timed_function(f, *args, **kwargs):
    myname = str(f).split(' ')[1]
    def new_func(*args, **kwargs):
        t = utime.ticks_us()
        result = f(*args, **kwargs)
        delta = utime.ticks_diff(utime.ticks_us(), t)
        print('Function {} Time = {:6.3f}ms'.format(myname, delta/1000))
        return result
    return new_func

def random_circle():
	for count in range(30):
		ox = random.randint(1, 126)
		oy = random.randint(1, 63)
		r = random.randint(5, 30)

		for i in range(360):
			oled.pixel(int(ox + r * math.cos(i)), int(oy + r * math.sin(i)), 1)

		oled.show()

def random_rect():
	for count in range(30):
		x = random.randint(1, 126)
		y = random.randint(1, 63)
		w = random.randint(1, 30)
		h = random.randint(1, 30)

		oled.rect(x, y, w, h, 1)
		oled.show()

def rects():
	w = 10
	h = 6

	while w < 128 and h < 64:
		x = int((128 - w) / 2)
		# y = int((64 - h) / 2) # center
		y = 64 - h # bottom

		oled.rect(x, y, w, h, 1)
		w += 6
		h += 3

		oled.show()

heart_large_list = [13, 0x71c,0x8a2,0x1041,0x1001,0x1001,0x1001,0x802,0x404,0x208,0x110,0xa0,0x40]
heart_middle_list = [11, 0x18c,0x252,0x421,0x401,0x401,0x202,0x104,0x88,0x50,0x20]
heart_small_list = [9, 0x6c,0x92,0x101,0x101,0x82,0x44,0x28,0x10]

heart_list = [heart_large_list, heart_middle_list, heart_small_list]


@timed_function
def heartbeat_1(start_x=20, start_y=20, repeat=10):
	heart_large = [
		'0011100011100',
		'0100010100010',
		'1000001000001',
		'1000000000001',
		'1000000000001',
		'1000000000001',
		'0100000000010',
		'0010000000100',
		'0001000001000',
		'0000100010000',
		'0000010100000',
		'0000001000000'
	]

	heart_middle = [
		'00110001100',
		'01001010010',
		'10000100001',
		'10000000001',
		'10000000001',
		'01000000010',
		'00100000100',
		'00010001000',
		'00001010000',
		'00000100000'
	]

	heart_small = [
		'001101100',
		'010010010',
		'100000001',
		'100000001',
		'010000010',
		'001000100',
		'000101000',
		'000010000'
	]

	for y in range(len(heart_large)):
		for x in range(len(heart_large[y])):
			oled.pixel(start_x + x, start_y + y, 1 if heart_large[y][x] == '1' else 0)

	heart_large_buf = bytearray(oled.get_buffer())
	oled.fill(0)

	start_x += (len(heart_large[0]) - len(heart_middle[0])) // 2
	start_y += (len(heart_large) - len(heart_middle)) // 2

	for y in range(len(heart_middle)):
		for x in range(len(heart_middle[y])):
			oled.pixel(start_x + x, start_y + y, 1 if heart_middle[y][x] == '1' else 0)

	heart_middle_buf = bytearray(oled.get_buffer())
	oled.fill(0)

	start_x += (len(heart_middle[0]) - len(heart_small[0])) // 2
	start_y += (len(heart_middle) - len(heart_small)) // 2

	for y in range(len(heart_small)):
		for x in range(len(heart_small[y])):
			oled.pixel(start_x + x, start_y + y, 1 if heart_small[y][x] == '1' else 0)

	heart_small_buf = bytearray(oled.get_buffer())

	gc.collect()

	# for count in range(repeat):
	# 	oled.set_buffer(heart_large_buf)
	# 	oled.show()
	# 	utime.sleep(0.6)

	# 	oled.set_buffer(heart_middle_buf)
	# 	oled.show()
	# 	utime.sleep(0.1)

	# 	oled.set_buffer(heart_small_buf)
	# 	oled.show()
	# 	utime.sleep(0.1)
	
	oled.poweroff()

@timed_function
def heartbeat_2(start_x=20, start_y=20, repeat=10):
	for y in range(len(heart_large_list) - 1):
		length = heart_large_list[0]

		line = str.replace(bin(heart_large_list[y + 1]), '0b', '')
		line = '0' * (length - len(line)) + line
		
		for x in range(length):
			oled.pixel(start_x + x, start_y + y, 1 if line[x] == '1' else 0)

	heart_large_buf = bytearray(oled.get_buffer())
	oled.fill(0)

	start_x += (heart_large_list[0] - heart_middle_list[0]) // 2
	start_y += (len(heart_large_list) - len(heart_middle_list)) // 2

	for y in range(len(heart_middle_list) - 1):
		length = heart_middle_list[0]

		line = str.replace(bin(heart_middle_list[y + 1]), '0b', '')
		line = '0' * (length - len(line)) + line
		
		for x in range(length):
			oled.pixel(start_x + x, start_y + y, 1 if line[x] == '1' else 0)

	heart_middle_buf = bytearray(oled.get_buffer())
	oled.fill(0)

	start_x += (heart_middle_list[0] - heart_small_list[0]) // 2
	start_y += (len(heart_middle_list) - len(heart_small_list)) // 2

	for y in range(len(heart_small_list) - 1):
		length = heart_small_list[0]

		line = str.replace(bin(heart_small_list[y + 1]), '0b', '')
		line = '0' * (length - len(line)) + line
		
		for x in range(length):
			oled.pixel(start_x + x, start_y + y, 1 if line[x] == '1' else 0)

	heart_small_buf = bytearray(oled.get_buffer())

	gc.collect()

	# for count in range(repeat):
	# 	oled.set_buffer(heart_large_buf)
	# 	oled.show()
	# 	utime.sleep(0.6)

	# 	oled.set_buffer(heart_middle_buf)
	# 	oled.show()
	# 	utime.sleep(0.1)

	# 	oled.set_buffer(heart_small_buf)
	# 	oled.show()
	# 	utime.sleep(0.1)
	
	oled.poweroff()

@timed_function
def heartbeat(start_x=20, start_y=20, repeat=10):
	heart_buffer = []

	for count, heart in enumerate(heart_list):
		t = utime.ticks_us()

		for y in range(len(heart) - 1):
			length = heart[0]

			line = str.replace(bin(heart[y + 1]), '0b', '')
			
			for x in range(length - 1, -1, -1):
				try:
					color = 1 if line[x - length] == '1' else 0
				except IndexError:
					color = 0

				oled.pixel(start_x + x, start_y + y, color)

		heart_buffer.append(bytearray(oled.get_buffer()))
		oled.fill(0)

		print('single: {}ms'.format(utime.ticks_diff(utime.ticks_us(), t) / 1000))

		if count < len(heart_list) - 1:
			start_x += (heart_list[count][0] - heart_list[count + 1][0]) // 2
			start_y += (len(heart_list[count]) - len(heart_list[count + 1])) // 2

		gc.collect()

	for count in range(repeat):
		for buffer in heart_buffer:
			oled.set_buffer(buffer)
			oled.show()
			utime.sleep(0.2)

	oled.poweroff()

def test_new_array():
	# heart = bytearray(b'<B\x81\x01\x01\x02\x04\x02\x01\x01\x81B<\x00\x00\x00\x01\x02\x04\x08\x04\x02\x01\x00\x00\x00')
	char = bytearray(b'\x1c\x1e\x13\x01\x01\x87\xfe|\x00\x00\x00\x80\xce\x81\x00\x00\x00\x00\x00\x01\x01\x01\x00\x00')
	buffer = framebuf.FrameBuffer(char, 8, 17, framebuf.MONO_VLSB)
	oled.fill(0)
	oled.blit(buffer, 20, 20)
	oled.show()




def main():
	global oled

	i2c = I2C(0, scl=Pin(18), sda=Pin(19))
	slave_list = i2c.scan()

	if slave_list:
		print('slave id: {}'.format(slave_list[0]))
		oled = SSD1306_I2C(128, 64, i2c)

		# random_circle()
		# random_rect()
		# rects()

		# heartbeat()
		# heartbeat_1()
		# heartbeat_2()
		# heartbeat_3()
		test_new_array()

if __name__ == "__main__":
	main()
